/*
 *	dfrow.c
 *	© 2009 DigiFi Limited
 *
 *	Contains the functions that get rows
 *
 */

#include "../headers/dfrow.h"

#define DFROW_MAX_SEARCHNUMBERS		(sizeof(unsigned int) * 8)

// variables for allocating which search number to use and a store
// for the call back
static unsigned int occupied_numbers = 0;
static unsigned int search_numbers = 0;
pthread_mutex_t dfrow_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t dfrow_cond = PTHREAD_COND_INITIALIZER;

// hold the divisor to split up the row pulling
static unsigned int row_divisor = 1;

static int dfrow_allocate_searchnumber(void);
static void dfrow_deallocate_searchnumber(int id);


void dfrow_setdivisor(unsigned int divisor) { row_divisor = (divisor<1) ? 1 : divisor; }


// system


int dfrow_rooms(int local_only, void (*callback)(df_rows *, df_roomrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,"[GetRooms %d %d]", search_num, (local_only>0) ? 1 : 0);
}

int dfrow_advancedstatus(void (*callback)(df_rows *, df_keyvaluerow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,"[GetAdvancedStatus %d]", search_num);
}







// artists


int dfrow_artists(int allocated, void (*callback)(df_rows *, df_artistrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,"[GetArtists %d %d]", search_num, (allocated>0) ? 1 : 0);
}







// albums / playlists


int dfrow_albums(char *sort_column, char *sort_order, void (*callback)(df_rows *, df_albumrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,"[GetAlbums %d \"%s\" \"%s\"]", search_num, sort_column, sort_order);
}


int dfrow_albumdetails(char *address, void (*callback)(df_rows *, df_albumdetailrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,"[GetAlbumDetails %d \"%s\"]", search_num, address);
}


int dfrow_albumsforartists(char *address, char *sort_column, char *sort_order, void (*callback)(df_rows *, df_albumrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetAlbumsForArtists %d \"%s\" \"%s\" \"%s\"]", search_num, address, sort_column, sort_order);
}

int dfrow_playlistsforsubgenre(char *address, char *sort_column, char *sort_order, void (*callback)(df_rows *, df_albumrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetPlaylistsForSubGenre %d \"%s\" \"%s\" \"%s\"]", search_num, address, sort_column, sort_order);
}

int dfrow_userplaylists(char *sort_column, char *sort_order, void (*callback)(df_rows *, df_albumrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetUserPlaylists %d \"%s\" \"%s\"]", search_num, sort_column, sort_order);
}





// tracks


int dfrow_tracksforplaylist(char *address, void (*callback)(df_rows *, df_trackrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetTracksForPlaylist %d \"%s\"]", search_num, address);
}

int dfrow_tracks(void (*callback)(df_rows *, df_trackrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetTracksAll %d]", search_num);
}

int dfrow_exttracksbycontributor(char *address, int type, char *album, void (*callback)(df_rows *, df_trackrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetExtTracksByContributor %d \"%s\" %d \"%s\"]", search_num, address, type, album);
}

int dfrow_tracksotherinfo(char *address, void (*callback)(df_rows *, df_trackinforow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetTracksOtherInfo %d \"%s\"]", search_num, address);
}

int dfrow_currentplaylist(int room_id, void (*callback)(df_rows *, df_trackrow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequestroom", (void (*)(void))callback,0,-1,1,0,
					"[GetCurrentPlayList %d %d]", search_num, room_id);
}





// genres


int dfrow_genres(int allocated, char *sort_column, char *sort_order, void (*callback)(df_rows *, df_genrerow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetGenres %d %d \"%s\" \"%s\"]", search_num, (allocated>0) ? 1 : 0, sort_column, sort_order);
}

int dfrow_subgenresforgenre(char *address, int allocated, char *sort_column, char *sort_order, void (*callback)(df_rows *, df_subgenrerow*)) {
	int search_num;
	
	if ((search_num = dfrow_allocate_searchnumber()) < 0) { return -1; }
	
	return dfmultithread_queue_row("rowrequest", (void (*)(void))callback,0,-1,1,0,
					"[GetSubGenresForGenre %d \"%s\" %d \"%s\" \"%s\"]", search_num, address, (allocated>0) ? 1 : 0, sort_column, sort_order);
}





int dfrow_callbackhandler(char *regex_name, dfrx_result **rx, void (*callback)(void), df_rows *pull_info) {
	int i = 0;
	df_artistrow *ar, *ar_current;
	df_roomrow *rr, *rr_current;
	df_trackrow *tr, *tr_current;
	df_albumrow *alr, *alr_current;
	df_albumdetailrow *adr, *adr_current;
	df_keyvaluerow *kvr, *kvr_current;
	df_genrerow *gr, *gr_current;
	df_subgenrerow *sgr, *sgr_current;
	df_trackinforow *tir, *tir_current;
	
	dferrors_printdebug("Received %d rows\n", pull_info->count);
	
	if (strcmp(regex_name, "GetArtists") == 0) {
		if (rx!=NULL) {
			ar = malloc(sizeof(df_artistrow));
			sscanf(rx[i]->subexps[3].value,"%d", &(ar->key));
			ar->name = strdup(rx[i]->subexps[4].value);
		
			ar->next = NULL;
		
			ar_current = ar;
		
			for (i=1; i<pull_info->count; i++) {
				ar_current->next = malloc(sizeof(df_artistrow));
				sscanf(rx[i]->subexps[3].value,"%d", &(ar_current->next->key));
				ar_current->next->name = strdup(rx[i]->subexps[4].value);
			
				ar_current->next->next = NULL;
			
				ar_current = ar_current->next;
			}
		}
		else { ar = NULL; }
		
		((void (*)(df_rows *, df_artistrow*))callback)(pull_info, ar);
	}
	else if (strcmp(regex_name, "GetCurrentPlayList") == 0) {
		if (rx!=NULL) {
			tr = malloc(sizeof(df_trackrow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &tr->key);
			tr->name = strdup(rx[i]->subexps[4].value);
			tr->sort_name = strdup(rx[i]->subexps[5].value);
			tr->type = strdup(rx[i]->subexps[6].value);
			sscanf(rx[i]->subexps[7].value, "%u:%u:%u", &tr->length.hours, &tr->length.minutes, &tr->length.seconds);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &tr->created_date.year, &tr->created_date.month, &tr->created_date.day);
			sscanf(rx[i]->subexps[9].value, "%u-%u-%u", &tr->last_modified_date.year, &tr->last_modified_date.month, &tr->last_modified_date.day);
			sscanf(rx[i]->subexps[10].value, "%d", &tr->ordinal);
			sscanf(rx[i]->subexps[11].value, "%d", &tr->extended_avaliable);
			sscanf(rx[i]->subexps[12].value, "%d", (int*)&tr->storage);
			sscanf(rx[i]->subexps[13].value, "%d", &tr->sample_rate);
			sscanf(rx[i]->subexps[14].value, "%d", &tr->bit_per_sample);
			sscanf(rx[i]->subexps[15].value, "%d", &tr->channel_count);
			sscanf(rx[i]->subexps[16].value, "%d", &tr->source);
		
			tr->next = NULL;
		
			tr_current = tr;
		
			for (i=1; i<pull_info->count; i++) {
				tr_current->next = malloc(sizeof(df_trackrow));
			
				sscanf(rx[i]->subexps[3].value, "%d", &tr_current->next->key);
				tr_current->next->name = strdup(rx[i]->subexps[4].value);
				tr_current->next->sort_name = strdup(rx[i]->subexps[5].value);
				tr_current->next->type = strdup(rx[i]->subexps[6].value);
				sscanf(rx[i]->subexps[7].value, "%u:%u:%u", 
					&tr_current->next->length.hours, &tr_current->next->length.minutes, &tr_current->next->length.seconds);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
					&tr_current->next->created_date.year, &tr_current->next->created_date.month, &tr_current->next->created_date.day);
				sscanf(rx[i]->subexps[9].value, "%u-%u-%u", 
					&tr_current->next->last_modified_date.year, &tr_current->next->last_modified_date.month, &tr_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[10].value, "%d", &tr_current->next->ordinal);
				sscanf(rx[i]->subexps[11].value, "%d", &tr_current->next->extended_avaliable);
				sscanf(rx[i]->subexps[12].value, "%d", (int*)&tr_current->next->storage);
				sscanf(rx[i]->subexps[13].value, "%d", &tr_current->next->sample_rate);
				sscanf(rx[i]->subexps[14].value, "%d", &tr_current->next->bit_per_sample);
				sscanf(rx[i]->subexps[15].value, "%d", &tr_current->next->channel_count);
				sscanf(rx[i]->subexps[16].value, "%d", &tr_current->next->source);
			
				tr_current->next->next = NULL;
			
				tr_current = tr_current->next;
			}
		}
		else { tr = NULL; }
		
		((void (*)(df_rows *, df_trackrow*))callback)(pull_info, tr);
	}
	else if (strcmp(regex_name, "GetTracksOtherInfo") == 0) {
		if (rx!=NULL) {
			tir = malloc(sizeof(df_trackinforow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &tir->key);
			tir->name = strdup(rx[i]->subexps[4].value);
			tir->sort_name = strdup(rx[i]->subexps[5].value);
			tir->type = strdup(rx[i]->subexps[6].value);
			sscanf(rx[i]->subexps[7].value, "%u:%u:%u", &tir->length.hours, &tir->length.minutes, &tir->length.seconds);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &tir->created_date.year, &tir->created_date.month, &tir->created_date.day);
			sscanf(rx[i]->subexps[9].value, "%u-%u-%u", &tir->last_modified_date.year, &tir->last_modified_date.month, &tir->last_modified_date.day);
			sscanf(rx[i]->subexps[10].value, "%d", &tir->ordinal);
			sscanf(rx[i]->subexps[11].value, "%d", &tir->extended_avaliable);
			sscanf(rx[i]->subexps[12].value, "%d", (int*)&tir->storage);
			sscanf(rx[i]->subexps[13].value, "%d", &tir->sample_rate);
			sscanf(rx[i]->subexps[14].value, "%d", &tir->bits_per_sample);
			sscanf(rx[i]->subexps[15].value, "%d", &tir->channel_count);
			sscanf(rx[i]->subexps[16].value, "%d", &tir->album_key);
			tir->album_name = strdup(rx[i]->subexps[17].value);
			sscanf(rx[i]->subexps[18].value, "%d", &tir->artist_key);
			tir->artist_name = strdup(rx[i]->subexps[19].value);
		
			tir->next = NULL;
		
			tir_current = tir;
		
			for (i=1; i<pull_info->count; i++) {
				tir_current->next = malloc(sizeof(df_trackinforow));

				sscanf(rx[i]->subexps[3].value, "%d", &tir_current->next->key);
				tir_current->next->name = strdup(rx[i]->subexps[4].value);
				tir_current->next->sort_name = strdup(rx[i]->subexps[5].value);
				tir_current->next->type = strdup(rx[i]->subexps[6].value);
				sscanf(rx[i]->subexps[7].value, "%u:%u:%u", 
						&tir_current->next->length.hours, &tir_current->next->length.minutes, &tir_current->next->length.seconds);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
						&tir_current->next->created_date.year, &tir_current->next->created_date.month, &tir_current->next->created_date.day);
				sscanf(rx[i]->subexps[9].value, "%u-%u-%u", 
						&tir_current->next->last_modified_date.year, &tir_current->next->last_modified_date.month, &tir_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[10].value, "%d", &tir_current->next->ordinal);
				sscanf(rx[i]->subexps[11].value, "%d", &tir_current->next->extended_avaliable);
				sscanf(rx[i]->subexps[12].value, "%d", (int*)&tir_current->next->storage);
				sscanf(rx[i]->subexps[13].value, "%d", &tir_current->next->sample_rate);
				sscanf(rx[i]->subexps[14].value, "%d", &tir_current->next->bits_per_sample);
				sscanf(rx[i]->subexps[15].value, "%d", &tir_current->next->channel_count);
				sscanf(rx[i]->subexps[16].value, "%d", &tir_current->next->album_key);
				tir_current->next->album_name = strdup(rx[i]->subexps[17].value);
				sscanf(rx[i]->subexps[18].value, "%d", &tir_current->next->artist_key);
				tir_current->next->artist_name = strdup(rx[i]->subexps[19].value);

				tir_current->next->next = NULL;

				tir_current = tir_current->next;
			}
		}
		else { tir = NULL; }
		
		((void (*)(df_rows *, df_trackinforow*))callback)(pull_info, tir);
	}
	else if (strcmp(regex_name, "GetAdvancedStatus") == 0) {
		if (rx!=NULL) {
			kvr = malloc(sizeof(df_keyvaluerow));
			kvr->key = strdup(rx[i]->subexps[3].value);
			kvr->value = strdup(rx[i]->subexps[4].value);
		
			kvr->next = NULL;
		
			kvr_current = kvr;
		
			for (i=1; i<pull_info->count; i++) {
				kvr_current->next = malloc(sizeof(df_keyvaluerow));
				kvr_current->next->key = strdup(rx[i]->subexps[3].value);
				kvr_current->next->value = strdup(rx[i]->subexps[4].value);
			
				kvr_current->next->next = NULL;
			
				kvr_current = kvr_current->next;
			}
		}
		else { kvr = NULL; }
		
		((void (*)(df_rows *, df_keyvaluerow*))callback)(pull_info, kvr);
	}
	else if (strcmp(regex_name, "GetRooms") == 0) {	
		if (rx!=NULL) {	
			rr = malloc(sizeof(df_roomrow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &rr->key);
			rr->ipaddress = strdup(rx[i]->subexps[4].value);
			sscanf(rx[i]->subexps[5].value, "%d", &rr->channel);
			rr->name = strdup(rx[i]->subexps[6].value);
			rr->short_name = strdup(rx[i]->subexps[7].value);
			sscanf(rx[i]->subexps[8].value, "%u", &rr->is_multiroom);
			sscanf(rx[i]->subexps[9].value, "%u", &rr->playback_capability);
			sscanf(rx[i]->subexps[10].value, "%u", &rr->ripping_capability);
			sscanf(rx[i]->subexps[11].value, "%u", &rr->musicmanagement_capabaility);
			sscanf(rx[i]->subexps[12].value, "%d", &rr->id);
			rr->host_name = strdup(rx[i]->subexps[13].value);
		
			rr->next = NULL;
		
			rr_current = rr;
		
			for (i=1; i<pull_info->count; i++) {
				rr_current->next = malloc(sizeof(df_roomrow));

				sscanf(rx[i]->subexps[3].value, "%d", &rr_current->next->key);
				rr_current->next->ipaddress = strdup(rx[i]->subexps[4].value);
				sscanf(rx[i]->subexps[5].value, "%d", &rr_current->next->channel);
				rr_current->next->name = strdup(rx[i]->subexps[6].value);
				rr_current->next->short_name = strdup(rx[i]->subexps[7].value);
				sscanf(rx[i]->subexps[8].value, "%u", &rr_current->next->is_multiroom);
				sscanf(rx[i]->subexps[9].value, "%u", &rr_current->next->playback_capability);
				sscanf(rx[i]->subexps[10].value, "%u", &rr_current->next->ripping_capability);
				sscanf(rx[i]->subexps[11].value, "%u", &rr_current->next->musicmanagement_capabaility);
				sscanf(rx[i]->subexps[12].value, "%d", &rr_current->next->id);
				rr_current->next->host_name = strdup(rx[i]->subexps[13].value);
			
				rr_current->next->next = NULL;
			
				rr_current = rr_current->next;
			}
		}
		else { rr = NULL; }
		
		((void (*)(df_rows *, df_roomrow*))callback)(pull_info, rr);
	}
	else if ((strcmp(regex_name, "GetAlbums") == 0) || (strcmp(regex_name, "GetAlbumsForArtists") == 0) ||
				(strcmp(regex_name, "GetPlaylistsForSubGenre") == 0) || (strcmp(regex_name, "GetUserPlaylists") == 0)) {	
		
		if (rx!=NULL) {	
			alr = malloc(sizeof(df_albumrow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &alr->key);
			alr->name = strdup(rx[i]->subexps[4].value);
			sscanf(rx[i]->subexps[5].value, "%d", &alr->artist_key);
			alr->artist_name = strdup(rx[i]->subexps[6].value);
			sscanf(rx[i]->subexps[7].value, "%d", &alr->is_album);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &alr->created_date.year, &alr->created_date.month, &alr->created_date.day);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &alr->last_modified_date.year, &alr->last_modified_date.month, &alr->last_modified_date.day);
			sscanf(rx[i]->subexps[10].value, "%d", &alr->hit_count);
			sscanf(rx[i]->subexps[11].value, "%d", &alr->extended_avaliable);
			sscanf(rx[i]->subexps[12].value, "%u", (unsigned int*)&alr->storage);
		
			alr->next = NULL;
		
			alr_current = alr;
		
			for (i=1; i<pull_info->count; i++) {
				alr_current->next = malloc(sizeof(df_albumrow));

				sscanf(rx[i]->subexps[3].value, "%d", &alr_current->next->key);
				alr_current->next->name = strdup(rx[i]->subexps[4].value);
				sscanf(rx[i]->subexps[5].value, "%d", &alr_current->next->artist_key);
				alr_current->next->artist_name = strdup(rx[i]->subexps[6].value);
				sscanf(rx[i]->subexps[7].value, "%d", &alr_current->next->is_album);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
					&alr_current->next->created_date.year, &alr_current->next->created_date.month, &alr_current->next->created_date.day);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
					&alr_current->next->last_modified_date.year, &alr_current->next->last_modified_date.month, &alr_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[10].value, "%d", &alr_current->next->hit_count);
				sscanf(rx[i]->subexps[11].value, "%d", &alr_current->next->extended_avaliable);
				sscanf(rx[i]->subexps[12].value, "%u", (unsigned int*)&alr_current->next->storage);

				alr_current->next->next = NULL;

				alr_current = alr_current->next;
			}
		}
		else { alr = NULL; }
		
		((void (*)(df_rows *, df_albumrow*))callback)(pull_info, alr);
	}
	else if (strcmp(regex_name, "GetAlbumDetails") == 0) {	
		
		if (rx!=NULL) {
			adr = malloc(sizeof(df_albumdetailrow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &adr->key);
			adr->name = strdup(rx[i]->subexps[4].value);
			sscanf(rx[i]->subexps[5].value, "%d", &adr->artist_key);
			adr->artist_name = strdup(rx[i]->subexps[6].value);
			sscanf(rx[i]->subexps[7].value, "%d", &adr->is_album);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &adr->created_date.year, &adr->created_date.month, &adr->created_date.day);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &adr->last_modified_date.year, &adr->last_modified_date.month, &adr->last_modified_date.day);
			sscanf(rx[i]->subexps[10].value, "%d", &adr->hit_count);
			sscanf(rx[i]->subexps[11].value, "%d", &adr->extended_avaliable);
			sscanf(rx[i]->subexps[12].value, "%u", (unsigned int*)&adr->storage);
			adr->genre = strdup(rx[i]->subexps[13].value);
			sscanf(rx[i]->subexps[14].value, "%d", &adr->track_count);
			adr->cover_image = strdup(rx[i]->subexps[15].value);
		
			adr->next = NULL;
		
			adr_current = adr;
		
			for (i=1; i<pull_info->count; i++) {
				adr_current->next = malloc(sizeof(df_albumdetailrow));

				sscanf(rx[i]->subexps[3].value, "%d", &adr_current->next->key);
				adr_current->next->name = strdup(rx[i]->subexps[4].value);
				sscanf(rx[i]->subexps[5].value, "%d", &adr_current->next->artist_key);
				adr_current->next->artist_name = strdup(rx[i]->subexps[6].value);
				sscanf(rx[i]->subexps[7].value, "%d", &adr_current->next->is_album);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
					&adr_current->next->created_date.year, &adr_current->next->created_date.month, &adr_current->next->created_date.day);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
					&adr_current->next->last_modified_date.year, &adr_current->next->last_modified_date.month, &adr_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[10].value, "%d", &adr_current->next->hit_count);
				sscanf(rx[i]->subexps[11].value, "%d", &adr_current->next->extended_avaliable);
				sscanf(rx[i]->subexps[12].value, "%u", (unsigned int*)&adr_current->next->storage);
				adr_current->next->genre = strdup(rx[i]->subexps[13].value);
				sscanf(rx[i]->subexps[14].value, "%d", &adr_current->next->track_count);
				adr_current->next->cover_image = strdup(rx[i]->subexps[15].value);

				adr_current->next->next = NULL;

				adr_current = adr_current->next;
			}
		}
		else { adr = NULL; }
		
		((void (*)(df_rows *, df_albumdetailrow*))callback)(pull_info, adr);
	}
	else if ((strcmp(regex_name, "GetTracksForPlaylist") == 0) || (strcmp(regex_name, "GetTracksAll") == 0) ||
				(strcmp(regex_name, "GetExtTracksByContributor") == 0)) {	
					
		if (rx!=NULL) {	
			tr = malloc(sizeof(df_trackrow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &tr->key);
			tr->name = strdup(rx[i]->subexps[4].value);
			tr->sort_name = strdup(rx[i]->subexps[5].value);
			tr->type = strdup(rx[i]->subexps[6].value);
			sscanf(rx[i]->subexps[7].value, "%u:%u:%u", &tr->length.hours, &tr->length.minutes, &tr->length.seconds);
			sscanf(rx[i]->subexps[8].value, "%u-%u-%u", &tr->created_date.year, &tr->created_date.month, &tr->created_date.day);
			sscanf(rx[i]->subexps[9].value, "%u-%u-%u", &tr->last_modified_date.year, &tr->last_modified_date.month, &tr->last_modified_date.day);
			sscanf(rx[i]->subexps[10].value, "%d", &tr->ordinal);
			sscanf(rx[i]->subexps[11].value, "%d", &tr->extended_avaliable);
			sscanf(rx[i]->subexps[12].value, "%d", (int*)&tr->storage);
			sscanf(rx[i]->subexps[13].value, "%d", &tr->sample_rate);
			sscanf(rx[i]->subexps[14].value, "%d", &tr->bit_per_sample);
			sscanf(rx[i]->subexps[15].value, "%d", &tr->channel_count);
		
			tr->next = NULL;
		
			tr_current = tr;
		
			for (i=1; i<pull_info->count; i++) {
				tr_current->next = malloc(sizeof(df_trackrow));
			
				sscanf(rx[i]->subexps[3].value, "%d", &tr_current->next->key);
				tr_current->next->name = strdup(rx[i]->subexps[4].value);
				tr_current->next->sort_name = strdup(rx[i]->subexps[5].value);
				tr_current->next->type = strdup(rx[i]->subexps[6].value);
				sscanf(rx[i]->subexps[7].value, "%u:%u:%u", 
					&tr_current->next->length.hours, &tr_current->next->length.minutes, &tr_current->next->length.seconds);
				sscanf(rx[i]->subexps[8].value, "%u-%u-%u", 
					&tr_current->next->created_date.year, &tr_current->next->created_date.month, &tr_current->next->created_date.day);
				sscanf(rx[i]->subexps[9].value, "%u-%u-%u", 
					&tr_current->next->last_modified_date.year, &tr_current->next->last_modified_date.month, &tr_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[10].value, "%d", &tr_current->next->ordinal);
				sscanf(rx[i]->subexps[11].value, "%d", &tr_current->next->extended_avaliable);
				sscanf(rx[i]->subexps[12].value, "%d", (int*)&tr_current->next->storage);
				sscanf(rx[i]->subexps[13].value, "%d", &tr_current->next->sample_rate);
				sscanf(rx[i]->subexps[14].value, "%d", &tr_current->next->bit_per_sample);
				sscanf(rx[i]->subexps[15].value, "%d", &tr_current->next->channel_count);
			
				tr_current->next->next = NULL;
			
				tr_current = tr_current->next;
			}
		}
		else { tr = NULL; }
		
		((void (*)(df_rows *, df_trackrow*))callback)(pull_info, tr);
	}
	else if (strcmp(regex_name, "GetGenres") == 0) {
		if (rx!=NULL) {	
			gr = malloc(sizeof(df_genrerow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &gr->key);
			gr->name = strdup(rx[i]->subexps[4].value);
			sscanf(rx[i]->subexps[5].value, "%u-%u-%u", 
				&gr->created_date.year, &gr->created_date.month, &gr->created_date.day);
			sscanf(rx[i]->subexps[6].value, "%u-%u-%u", 
				&gr->last_modified_date.year, &gr->last_modified_date.month, &gr->last_modified_date.day);
			sscanf(rx[i]->subexps[7].value, "%d", &gr->ordinal);
			sscanf(rx[i]->subexps[8].value, "%d", (int*)&gr->storage);
		
			gr->next = NULL;
		
			gr_current = gr;
		
			for (i=1; i<pull_info->count; i++) {
				gr_current->next = malloc(sizeof(df_genrerow));
			
				sscanf(rx[i]->subexps[3].value, "%d", &gr_current->next->key);
				gr_current->next->name = strdup(rx[i]->subexps[4].value);
				sscanf(rx[i]->subexps[5].value, "%u-%u-%u", 
					&gr_current->next->created_date.year, &gr_current->next->created_date.month, &gr_current->next->created_date.day);
				sscanf(rx[i]->subexps[6].value, "%u-%u-%u", 
					&gr_current->next->last_modified_date.year, &gr_current->next->last_modified_date.month, &gr_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[7].value, "%d", &gr_current->next->ordinal);
				sscanf(rx[i]->subexps[8].value, "%d", (int*)&gr_current->next->storage);
			
				gr_current->next->next = NULL;

				gr_current = gr_current->next;
			}
		}
		else { gr = NULL; }
		
		((void (*)(df_rows *, df_genrerow*))callback)(pull_info, gr);
	}
	else if (strcmp(regex_name, "GetSubGenresForGenre") == 0) {	
		if (rx!=NULL) {	
			sgr = malloc(sizeof(df_subgenrerow));
		
			sscanf(rx[i]->subexps[3].value, "%d", &sgr->key);
			sgr->name = strdup(rx[i]->subexps[4].value);
			sscanf(rx[i]->subexps[5].value, "%u-%u-%u", 
				&sgr->created_date.year, &sgr->created_date.month, &sgr->created_date.day);
			sscanf(rx[i]->subexps[6].value, "%u-%u-%u", 
				&sgr->last_modified_date.year, &sgr->last_modified_date.month, &sgr->last_modified_date.day);
			sscanf(rx[i]->subexps[7].value, "%d", &sgr->ordinal);
		
			sgr->next = NULL;
		
			sgr_current = sgr;
		
			for (i=1; i<pull_info->count; i++) {
				sgr_current->next = malloc(sizeof(df_subgenrerow));
			
				sscanf(rx[i]->subexps[3].value, "%d", &sgr_current->next->key);
				sgr_current->next->name = strdup(rx[i]->subexps[4].value);
				sscanf(rx[i]->subexps[5].value, "%u-%u-%u", 
					&sgr_current->next->created_date.year, &sgr_current->next->created_date.month, &sgr_current->next->created_date.day);
				sscanf(rx[i]->subexps[6].value, "%u-%u-%u", 
					&sgr_current->next->last_modified_date.year, &sgr_current->next->last_modified_date.month, &sgr_current->next->last_modified_date.day);
				sscanf(rx[i]->subexps[7].value, "%d", &sgr_current->next->ordinal);
			
				sgr_current->next->next = NULL;

				sgr_current = sgr_current->next;
			}
		}
		else { sgr = NULL; }
		
		((void (*)(df_rows *, df_subgenrerow*))callback)(pull_info, sgr);
	}
	
	return 0;
}



void dfrow_pullrows(dfrx_result *rx, df_asyncpayload *payload) {
	int id, num_rows, rows_per_pull, rows_requested, row_offset;
	int i, err;
	
	// get the search number
	sscanf(rx->subexps[2].value, "%d", &id);
	
	// the amount of rows that have been returned
	if (strcmp(payload->regex_name, "rowrequestroom") == 0) {
		// hack for returns which include the room id
		sscanf(rx->subexps[4].value, "%d", &num_rows);
	}
	else {
		sscanf(rx->subexps[3].value, "%d", &num_rows);
	}
	
	
	
	// check if the number returned is 0
	if (num_rows == 0) {
		payload->pull_info->count = 0;
		dfrow_callbackhandler(rx->subexps[1].value, NULL, payload->callback, payload->pull_info);
		return;
	}
	
	// if not all of the rows have been requested
	if (payload->pull_info->total != -1) { 
		// then pull between the offset and the total
		// requested unless there arent that many rows
		if (num_rows<(payload->pull_info->total + payload->pull_info->offset)) {
			// if the number of rows are less than the ammount there should be
			
			if ((num_rows - payload->pull_info->offset) <=0) { dferrors_printerror(-2, "Ran out of items to request"); return;}
			else { num_rows = (num_rows - payload->pull_info->offset); }
		}
		else { num_rows = payload->pull_info->total; }
	}
	
	row_offset = payload->pull_info->offset;
	
	// work out the number of rows per pull
	rows_per_pull = num_rows / row_divisor;
	
	rows_requested = 0;
	
	for (i=0; i<row_divisor; i++) {
		if (rows_requested>=num_rows) { dferrors_printerror(-1, "More rows have been requested than returned"); }
		
		// deal with any remainders
		if (i+1 == row_divisor) {
			err = dfmultithread_queue_row(rx->subexps[1].value, payload->callback, id, num_rows, num_rows-rows_requested, rows_requested,
			 						"[GetRows %d %d %d]", id, row_offset + rows_requested+1, num_rows - rows_requested);
		}
		else {
			err = dfmultithread_queue_row(rx->subexps[1].value, payload->callback, id, num_rows, rows_per_pull, rows_requested,
			 						"[GetRows %d %d %d]", id, row_offset + rows_requested+1, num_rows - rows_requested);
		}
		
		if (err != 0) { dferrors_printerror(err, "Failed to queue row pull"); }
		
		rows_requested += rows_per_pull;
	}
	
	dfrow_deallocate_searchnumber(id);
}



static int dfrow_allocate_searchnumber(void){
	int i;
	
	while (!(occupied_numbers<DFROW_MAX_SEARCHNUMBERS)) { 
		pthread_cond_wait(&dfrow_cond, &dfrow_mutex); 
	}
	
	for (i=0; i<DFROW_MAX_SEARCHNUMBERS; i++) {
		if (((search_numbers>>i) & 1) == 0) {
			search_numbers = search_numbers | (1<<(i));
			pthread_mutex_unlock(&dfrow_mutex);
			return i + 1;
		}
	}
	
	pthread_mutex_unlock(&dfrow_mutex);
	
	return -1;
}


static void dfrow_deallocate_searchnumber(int id) {

	pthread_mutex_lock(&dfrow_mutex);
	
	search_numbers = search_numbers & ~(1<<(id-1));
	
	pthread_mutex_unlock(&dfrow_mutex);
	
	pthread_cond_signal(&dfrow_cond);
}


